package com.comleader.ldmapdownload.util.map;

import com.comleader.ldmapdownload.util.bean.Line;
import com.comleader.ldmapdownload.util.bean.Point;

import java.util.List;

/**
 * @ClassName MapUtil
 * @Description: 地图工具类
 * @Author zhanghang
 * @Date 2020/4/26
 * @Version V1.0
 **/
public class MapUtil {
	
    // 地球半径
    private static double EARTH_RADIUS = 6378.137;

    /**
     * 给定点和多边形，判断给定的点是否在多边形内
     * @param point
     * @param points
     * @return
     */
    public static boolean isPointInPolygon(Point point, List<Point> points) {


        boolean result = false;
        int intersectCount = 0;
        // 判断依据：求解从该点向右发出的水平线射线与多边形各边的交点，当交点数为奇数，则在内部
        //不过要注意几种特殊情况：1、点在边或者顶点上;2、点在边的延长线上;3、点出发的水平射线与多边形相交在顶点上
        /**
         * 具体步骤如下：
         * 循环遍历各个线段：
         *  1、判断点是否在当前边上(斜率相同,且该点的x值在两个端口的x值之间),若是则返回true
         *  2、否则判断由该点发出的水平射线是否与当前边相交,若不相交则continue
         *  3、若相交,则判断是否相交在顶点上(边的端点是否在给定点的水平右侧).若不在,则认为此次相交为穿越,交点数+1 并continue
         *  4、若交在顶点上,则判断上一条边的另外一个端点与当前边的另外一个端点是否分布在水平射线的两侧.若是则认为此次相交为穿越,交点数+1.
         */
        for (int i = 0; i < points.size(); i++) {
            Point pointA = points.get(i);
            Point pointB = null;
            Point pointPre = null;
            // 如果是该点与区域顶点重合
            if (point.equals(pointA)) return true;
            //若当前是第一个点,则上一点则是list里面的最后一个点
            if (i == 0) {
                pointPre = points.get(points.size() - 1);
            } else {
                pointPre = points.get(i - 1);
            }
            //若已经循环到最后一个点,则与之连接的是第一个点
            if (i == (points.size() - 1)) {
                pointB = points.get(0);
            } else {
                pointB = points.get(i + 1);
            }
            Line line = new Line(pointA, pointB);
            //1、判断点是否在当前边上(斜率相同,且该点的x值在两个端口的x值之间),若是则返回true
            boolean isAtLine = line.isContainsPoint(point);
            if (isAtLine) {
                return true;
            } else {
                //2、若不在边上,判断由该点发出的水平射线是否与当前边相交,若不相交则continue
                //设置该射线的另外一个端点的x值=999,保证边的x永远不超过
                Point  radialPoint = new Point(999d, point.getLat());
                Line radial = new Line(point, radialPoint);
                boolean isIntersect = radial.isIntersect(line);
                if (!isIntersect) {
                    continue;
                } else {
                    //3、若相交,则判断是否相交在顶点上(边的端点是否在给定点的水平右侧).若不在,则认为此次相交为穿越,交点数+1 并continue
                    if (!( (pointA.getLng() > point.getLng()) && (pointA.getLat().equals(point.getLat()))
                            || (pointB.getLng() > point.getLng()) && (pointB.getLat().equals(point.getLat())) )) {
                        intersectCount++;
                        continue;
                    } else {
                        //4、若交在顶点上,则判断上一条边的另外一个端点与当前边的另外一个端点是否分布在水平射线的两侧.若是则认为此次相交为穿越,交点数+1
                        if ((pointPre.getLat() - point.getLat()) * (pointB.getLat() - point.getLat()) < 0) {
                            intersectCount++;
                        }
                    }
                }
            }
        }
        result = intersectCount % 2 == 1;
        return result;
    }

    /**
     * @description 判断两个面是否出现重叠部分或者包含关系，注意：需要保证多边形端点的顺序
     * @author zhanghang
     * @date 2020-08-11 14:11:36
	 * @param polygon
		 * @param polygon2
     * @return
     **/
    public static boolean isCoinCide(List<Point> polygon, List<Point> polygon2) {
        //获取多边形1的线段
        List<Line> lineList = Line.getLines(polygon);
        //获取多边形2的线段
        List<Line> lineList2 = Line.getLines(polygon2);
        //判断是否相交
        boolean isIntersect = isIntersect(lineList, lineList2);
        if (isIntersect) return true;
        //如果不相交判断是否包含-由于没有相交线段只要存在点在多边形内就说明包含
        boolean isPolygonInPolygon = isPolygonInPolygon(polygon, polygon2);
        if (isPolygonInPolygon) return true;
        return false;
    }

    // 判断两个面是否重叠
    private static boolean isPolygonInPolygon(List<Point> polygon, List<Point> polygon2) {
        //判断第一个多边形是否在第二个多边形内
        for (Point point : polygon) {
            boolean isPointInPolygon = isPointInPolygon(point,polygon2);
            if (isPointInPolygon) return true;
        }
        //判断第二个多边形是否在第一个多边形内
        for (Point point : polygon2) {
            boolean isPointInPolygon = isPointInPolygon(point,polygon);
            if (isPointInPolygon) return true;
        }
        return false;
    }


    // 比较两个多边形是否有线段相交
    private static boolean isIntersect(List<Line> polygon, List<Line> polygon2) {
        for (Line line : polygon) {
            for (Line line1 : polygon2) {
                //两条线段是否相交
                boolean b = line.isIntersect(line1);
                if (b) {
                    return true;
                }
            }
        }
        return false;
    }


    /**
     * 通过经纬度获取距离(单位：米)
     * @param startPoint
     * @param endPoint
     * @return 距离
     */
    public static double getDistance(Point startPoint, Point endPoint) {
        double radLat1 = rad(startPoint.getLat());
        double radLat2 = rad(endPoint.getLat());
        double a = radLat1 - radLat2;
        double b = rad(startPoint.getLng()) - rad(endPoint.getLng());
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(radLat1) * Math.cos(radLat2)
                * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000d) / 10000d;
        s = s * 1000;
        return s;
    }

    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

}
